#pragma once
#include <iostream>
#include <unistd.h>
#include <vector>
#include <sstream>
#include <fstream>
#include <signal.h>
#include <unordered_map>

#include "socket.hpp"

const std::string sep = "\r\n";
const char header_sep = ':';
const char blank_sep = ' ';

const std::string home_path = "webroot";

std::unordered_map<std::string, std::string> ContentMap = {
    {".html", "text/html"}, {".text", "text/plain"}, {".png", "text/png"}};

class httpRequest
{
public:
    httpRequest()
    {
    }
    ~httpRequest()
    {
    }
    bool deserialization(std::string &massage)
    {
        while (true)
        {
            int pos = massage.find(sep);
            if (pos == std::string::npos)
            {
                return false;
            }
            if (pos == 0) // 说明找到的是空行
            {
                break;
            }
            _req.push_back(massage.substr(0, pos)); // 将每行划分出来
            massage.erase(0, pos + 2);              // 删除划分出来的行
        }
        // int pos=massage.find("Content-Length");
        // 本来我是打算查看报文中是否有Content-Length来判断body是否存在的
        // 但是还有Transfer-Encoding分块传输的存在，所以这样的方式是不够全面的
        // 所以我们直接认为浏览器发送的报文是完整的，也一般是完整的，因为这个服务器只有我们一个人使用
        _body = massage;

        // 我们还要将首行以及header中的属性数据解析出来，这是需要给服务器，服务器通过这些数据来进行响应处理的
        analysis();

        return true;
    }

    bool analysis()
    {
        std::stringstream s(_req[0]);
        s >> _method >> _URI >> _http_verson;

        // 处理URI把属性数据获取出来
        std::string tmp = _URI;
        int pos = tmp.find('?');
        if (pos != std::string::npos)
        {
            // lg(DEBUG, "%s,%d", tmp.c_str(), pos);
            _property = tmp.substr(pos + 1);
            tmp.erase(pos, std::string::npos);
        }
        pos = tmp.find('#');
        if (pos != std::string::npos)
        {
            tmp.erase(pos, std::string::npos);
        }

        _file_path = home_path;
        if (tmp == "/" || tmp == "/index.html")
        {
            _file_path += "/index.html";
        }
        else
        {
            _file_path += tmp;
        }

        return true;
    }

    void print()
    {
        for (auto &it : _req)
        {
            std::cout << it << std::endl;
        }
        std::cout << _body << std::endl;
        std::cout << "method: " << _method << std::endl;
        std::cout << "URI: " << _URI << std::endl;
        std::cout << "http_lesson: " << _http_verson << std::endl;
        std::cout << "_file_path: " << _file_path << std::endl;
        std::cout << "_property: " << _property << std::endl;
    }

    std::string getPath()
    {
        return _file_path;
    }

    std::string getContent()
    {
        int pos = _file_path.rfind('.');
        if (pos == std::string::npos)
            return ContentMap[".html"];
        std::string suffix = _file_path.substr(pos);
        return ContentMap[suffix];
    }

    void set_file_path(const std::string new_path) // 如果打开失败文件，说嘛请求错误，要重新修改访问的网站
    {
        _file_path = new_path;
    }

private:
    std::vector<std::string> _req;
    std::string _body;
    // 首行数据
    std::string _method;
    std::string _URI;
    std::string _http_verson;

    std::string _file_path; // 客户端想要访问的文件
    std::string _property;  // URI中的属性数据

    // header中的属性(我们暂时不需要所以不分析出来)
};

class response
{
public:
    response(const std::string &http_verson, int stat_code, const std::string &stat_describe)
        : _http_verson(http_verson), _stat_code(stat_code), _stat_describe(stat_describe)
    {
    }
    ~response()
    {
    }
    bool serialize(std::string &sendMassage, const std::string file_path, std::string Content = "text/html")
    {
        // 获取body
        std::string text;

        std::ifstream in(file_path.c_str(), std::ios_base::binary); // 这里一定要显式的使用二进制打开
        if (!in.is_open())
        {
            lg(WARNING, "open sendText fail");
            return false;
        }
        char buffer[1024];
        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        text.resize(len);
        in.read((char *)text.c_str(), len);

        in.close();
        // 首行内容
        sendMassage = _http_verson;
        sendMassage += blank_sep;
        sendMassage += std::to_string(_stat_code);
        sendMassage += blank_sep;
        sendMassage += _stat_describe;
        sendMassage += sep;
        // header内容

        sendMassage += "Content-Length: ";
        sendMassage += std::to_string(text.size());
        sendMassage += sep;

        sendMassage += "Location: https://www.baidu.com"; // 这里是302状态时重定向的网址
        sendMassage += sep;

        sendMassage += "Content-Type: "; // Content-Type属性
        sendMassage += Content;
        sendMassage += sep;

        sendMassage += "Set-Cookie: passwd=123456"; // Set-Cookie属性
        sendMassage += sep;
        sendMassage += "Set-Cookie: userName=zhangsan";
        sendMassage += sep;
        // 空行
        sendMassage += sep;

        // body内容

        sendMassage += text;
        // sendMassage += sep;//不需要在body部分加"\r\n"了因为，Content-Length属性已经标识了body部分的长度

        return true;
    }

private:
    std::string _http_verson;
    int _stat_code;
    std::string _stat_describe;
};

class server
{
public:
    server(uint16_t port)
        : _port(port)
    {
        init();
    }
    void init()
    {
        _sck.Bind(_port);
        _sck.Listen();

        // 下面的代码用来让服务器重启不需要等待时间，暂时不知道原理，先用着
        int opt = 1;
        setsockopt(_sck.getsock(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
    }

    void run()
    {
        signal(SIGPIPE, SIG_IGN);
        signal(SIGCHLD, SIG_IGN); // 让让主进程不要阻塞等待

        while (true)
        {
            std::string clientIp;
            uint16_t clientPort;
            int serverFd = _sck.Accept(clientIp, &clientPort);
            // int count = 0;
            if (fork() == 0)
            {
                std::string getMassage; // 获取客户端报文
                while (true)
                {
                    char buffer[1024] = {0};
                    int n = recv(serverFd, buffer, sizeof(buffer) - 1, 0);
                    if (n == 0)
                    {
                        lg(FATAL, "client close");
                        break;
                    }
                    // lg(DEBUG, "%d", ++count);
                    buffer[n] = '\0';
                    getMassage += buffer; // 客户端的所有报文请求

                    // 这里假设每次都能读取到完整的报文，否则我们还要对报文是否完整进行检查和处理
                    // std::cout<<getMassage<<std::flush;

                    httpRequest req;
                    req.deserialization(getMassage);
                    req.print(); //  这里可以查看我们浏览器发送来的请求
                    std::cout << "------------------------------" << std::endl;

                    // 下面我们来对请求做处理，并返回响应
                    response res("HTTP/1.1", 200, "OK");
                    // response res("HTTP/1.1", 302, "Found");//测试302状态码重定向

                    std::string sendMassage;
                    if (!(res.serialize(sendMassage, req.getPath(), req.getContent()))) // 如果序列化失败，代表没有找到响应的文件
                    {
                        response res1("HTTP/1.1", 404, "Not Found");
                        req.set_file_path("webroot/erro.html");
                        res1.serialize(sendMassage, req.getPath(), req.getContent());
                    }
                    // lg(DEBUG, "sendMassage:\n%s", sendMassage.c_str());
                    // std::cout << "------------------------------" << std::endl;
                    // std::cout << "------------------------------" << std::endl;

                    // 将处理好的响应发送回客户端（浏览器）
                    send(serverFd, sendMassage.c_str(), sendMassage.size(), 0);
                }

                _sck.Close();
                exit(-1);
            }
            close(serverFd);
        }
    }

private:
    uint16_t _port;
    Socket _sck;
};